<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2024/8/12 0012
 * Time: 17:48
 */

namespace ShipSun\WechatMini;

use GuzzleHttp\RequestOptions;
use Psr\Http\Message\ResponseInterface;
use ReflectionClass;
use ReflectionProperty;
use ShipSun\WechatMini\Requests\RequestInterface;

/**
 * Class Client
 * @package ShipSun\WechatMini
 */
class Client
{
    /**
     * @var \GuzzleHttp\Client
     */
    protected $client;
    /**
     * @var array
     */
    protected $config;

    /**
     * Client constructor.
     * @param array $config
     * @throws \GuzzleHttp\Exception\InvalidArgumentException
     * @throws \InvalidArgumentException
     */
    public function __construct(array $config)
    {
        $this->client = new \GuzzleHttp\Client();
        $this->config = $config;
    }

    /**
     * @param \ShipSun\WechatMini\Requests\RequestInterface $request
     * @param string                                        $token
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \ReflectionException
     * @throws \RuntimeException
     * @throws \ShipSun\WechatMini\ResponseException
     */
    public function run(RequestInterface $request, string $token = null)
    {
        $options = $this->defaultOptions();

        $options = $this->fillData($request, $options);
        $response = $this->client->request($request->getMethod(), $this->getUrl($request, $token), $options);
        if ($response->getStatusCode() != '200') {
            throw new ResponseException($response->getReasonPhrase(), $response->getStatusCode());
        }
        return $this->parseResponse($response);
    }

    /**
     * @return mixed
     */
    protected function defaultOptions()
    {
        $options[RequestOptions::HEADERS] = ['Accept' => 'application/json'];
        return $options;
    }

    /**
     * @param \ShipSun\WechatMini\Requests\RequestInterface $request
     * @param array                                         $options
     * @return array
     * @throws \ReflectionException
     */
    protected function fillData(RequestInterface $request, array $options)
    {
        $data = $this->getRequestData($request);
        if (!$request->isAccessToken()) {
            $data['appid'] = $this->config('appid');
            $data['secret'] = $this->config('secret');
        }

        $method = $request->getMethod();
        if (strtolower($method) == 'get') {
            $options[RequestOptions::QUERY] = $data;
        }

        if (strtolower($method) == 'post') {
            $options[RequestOptions::HEADERS]['Content-Type'] = 'application/json';
            $options[RequestOptions::JSON] = $data;
        }
        return $options;
    }

    /**
     * @param \ShipSun\WechatMini\Requests\RequestInterface $request
     * @return array
     * @throws \ReflectionException
     */
    protected function getRequestData(RequestInterface $request)
    {
        $reflectionClass = new ReflectionClass($request);
        $properties = $reflectionClass->getProperties(ReflectionProperty::IS_PUBLIC);
        $data = [];
        foreach ($properties as $property) {
            $data[$property->name] = $property->getValue($request);
        }
        return array_filter($data);
    }

    /**
     * @param $key
     * @return mixed|null
     */
    protected function config($key)
    {
        return $this->config[$key] ?? null;
    }

    /**
     * @param \ShipSun\WechatMini\Requests\RequestInterface $request
     * @param string                                        $token
     * @return string
     */
    protected function getUrl(RequestInterface $request, string $token = null)
    {
        if ($request->isAccessToken()) {
            return $request->getUrl() . '?access_token=' . $token;
        }
        return $request->getUrl();
    }

    /**
     * @param \Psr\Http\Message\ResponseInterface $response
     * @return array
     * @throws \RuntimeException
     */
    protected function parseResponse(ResponseInterface $response)
    {
        $content = $response->getBody()->getContents();
        return json_decode($content, true);
    }
}
